
package org.movee.spring.data.hugegraph.mapping;

import org.movee.spring.data.hugegraph.annotation.Edge;
import org.movee.spring.data.hugegraph.annotation.Vertex;
import org.movee.spring.data.hugegraph.exception.HugeEntityInformationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.ArrayList;
import java.util.List;


@Slf4j
public class BasicHugePersistentEntity<T> extends BasicPersistentEntity<T, HugePersistentProperty>
        implements HugePersistentEntity<T>, ApplicationContextAware {

    private final StandardEvaluationContext context;

    private final HugeEntityType entityType;
    private final String label;

    private HugePersistentProperty edgeHeadProperty;
    private HugePersistentProperty edgeTailProperty;

    public BasicHugePersistentEntity(TypeInformation<T> information) {
        super(information);

        this.context = new StandardEvaluationContext();

        Vertex vertex = super.findAnnotation(Vertex.class);
        Edge edge = super.findAnnotation(Edge.class);

        if (vertex != null) {
            this.entityType = HugeEntityType.VERTEX;
            this.label = vertex.label();
        } else if (edge != null) {
            this.entityType = HugeEntityType.EDGE;
            this.label = edge.label();
        } else {
            throw new HugeEntityInformationException("entity must be vertex or edge, not both or none");
        }


    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context.addPropertyAccessor(new BeanFactoryAccessor());
        this.context.setBeanResolver(new BeanFactoryResolver(applicationContext));
        this.context.setRootObject(applicationContext);
    }

    /*
    @Override
    public Boolean isNew(final T bean) {

        IdentifierAccessor idAccessor = getIdentifierAccessor(bean);
        return idAccessor.getIdentifier() == null;
    }
    */

    @Override
    public HugeEntityType getEntityType() {
        return this.entityType;
    }

    @Override
    public String getLabel() {
        return this.label;
    }

    @Override
    public Object getId(Object bean) {
        return super.getIdentifierAccessor(bean).getIdentifier();
    }

    @Override
    public List<HugePersistentProperty> getPersistentProperties() {

        final List<HugePersistentProperty> properties = new ArrayList<>();
        super.doWithProperties((HugePersistentProperty p) -> properties.add(p));

        return properties;
    }

    @Override
    public HugePersistentProperty getEdgeHeadProperty() {
        return edgeHeadProperty;
    }

    @Override
    public HugePersistentProperty getEdgeTailProperty() {
        return edgeTailProperty;
    }

    @Override
    public void addPersistentProperty(HugePersistentProperty property) {
        super.addPersistentProperty(property);

        if (property.isEdgeHeadProperty()) {
            this.edgeHeadProperty = property;
        }

        if (property.isEdgeTailProperty()) {
            this.edgeTailProperty = property;
        }
    }
}
